#include "fe25519.h"

void fe25519_pow2523(fe25519 *r, const fe25519 *x) {
  fe25519 z2;
  fe25519 z9;
  fe25519 z11;
  fe25519 z2_5_0;
  fe25519 z2_10_0;
  fe25519 z2_20_0;
  fe25519 z2_50_0;
  fe25519 z2_100_0;
  fe25519 t;

  /* 2 */ fe25519_square(&z2, x);
  /* 4 */ fe25519_square(&t, &z2);
  /* 8 */ fe25519_square(&t, &t);
  /* 9 */ fe25519_mul(&z9, &t, x);
  /* 11 */ fe25519_mul(&z11, &z9, &z2);
  /* 22 */ fe25519_square(&t, &z11);
  /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0, &t, &z9);

  /* 2^6 - 2^1 */ fe25519_square(&t, &z2_5_0);
  /* 2^10 - 2^5 */ fe25519_nsquare(&t, 4);
  /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0, &t, &z2_5_0);

  /* 2^11 - 2^1 */ fe25519_square(&t, &z2_10_0);
  /* 2^20 - 2^10 */ fe25519_nsquare(&t, 9);
  /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0, &t, &z2_10_0);

  /* 2^21 - 2^1 */ fe25519_square(&t, &z2_20_0);
  /* 2^40 - 2^20 */ fe25519_nsquare(&t, 19);
  /* 2^40 - 2^0 */ fe25519_mul(&t, &t, &z2_20_0);

  /* 2^41 - 2^1 */ fe25519_square(&t, &t);
  /* 2^50 - 2^10 */ fe25519_nsquare(&t, 9);
  /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0, &t, &z2_10_0);

  /* 2^51 - 2^1 */ fe25519_square(&t, &z2_50_0);
  /* 2^100 - 2^50 */ fe25519_nsquare(&t, 49);
  /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0, &t, &z2_50_0);

  /* 2^101 - 2^1 */ fe25519_square(&t, &z2_100_0);
  /* 2^200 - 2^100 */ fe25519_nsquare(&t, 99);
  /* 2^200 - 2^0 */ fe25519_mul(&t, &t, &z2_100_0);

  /* 2^201 - 2^1 */ fe25519_square(&t, &t);
  /* 2^250 - 2^50 */ fe25519_nsquare(&t, 49);
  /* 2^250 - 2^0 */ fe25519_mul(&t, &t, &z2_50_0);

  /* 2^251 - 2^1 */ fe25519_square(&t, &t);
  /* 2^252 - 2^2 */ fe25519_square(&t, &t);
  /* 2^252 - 3 */ fe25519_mul(r, &t, x);
}
